/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ace.game.entity.enemy;

import ace.game.Path;
import java.util.Vector;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import ace.game.Sprite;
import ace.game.entity.Entity;
import ace.game.entity.LivingEntity;
import ace.game.entity.bullet.Bullet;
import ace.game.entity.player.Player;
import ace.geometry.Vector2D;
import ace.geometry.Vertex2D;
import ace.system.StateManager;
import ace.system.Utilities;
import ace.system.xml.XmlNode;

/**
 *
 * @author Hallucinogen
 */
public class Enemy extends LivingEntity {

	protected Enemy() {
		//Initialize
		mHealth		= 1;
        mVelocity   = new Vector2D();

		//No array
		mPath		= null;
		mAlive		= true;

        // spawning stuffs
		mSpawned	= false;
		mSpawnTime	= 0;
        mShouldExplode = true;

        // pathing stuffs
        mLastPathingTime    = 0;
        mLastPathingIndex   = 0;

        // shooting stuffs
        mTarget         = null;
        mReloadTime     = 0;
		mBullets		= new Vector();
		mDeleteBulles	= new Vector();
        mWaveReloadTime = 0;
        mWaveShotCount  = 0;
	}

	public Enemy(EnemyData data, Vertex2D inPos, int spawnTime) {
		this(data, new Path(new Vertex2D[]{inPos}, new int[]{0}), spawnTime);
	}

	public Enemy(EnemyData data, Path path, int spawnTime) {
		this(data, path, spawnTime, -1);
	}

    public Enemy(EnemyData data, Path path, int spawnTime, int pickup) {
        //Initialize
		this();

		//Initialize data
		position	= path.getPositions(0);
		mData		= data;
		mPath		= path;
        mHealth		= mData.getHealthMax();
		mSpawnTime	= spawnTime;
        mPickupData = pickup;
    }

	public void spawn() {
		mSpawned	= true;
		mSprite		= mData.getSprite().clone();
	}

	public EnemyData getData()		{	return mData;									}
	public boolean canSpawn(int y)	{	return y < mSpawnTime * Utilities.TILE_HEIGHT;	}
	public boolean isSpawned()		{	return mSpawned;								}
    public Sprite getSprite()       {   return mSprite;                                 }
    public Vector getBullets()      {   return mBullets;                                }
    public int getPickupData()      {   return mPickupData;                             }
    public boolean isShouldExplode(){   return mShouldExplode;                          }

	public void setTarget(Player target) {
        if (mTarget == null) {
            mTarget = target;
        }
    }

	public void update(long time) {
		if (!mSpawned) return;

        // update sprite
		mData.getSprite().update(time);

        // update target
        if (mTarget != null && !mTarget.isAlive()) {
            mTarget = null;
        }
        if (mData.mSeeking && mTarget != null) {
            moveEnemy(mTarget.position, time);
        } else {
            updatePathing(time);
        }

        // update shooting
        if (mTarget != null && mData.getBulletData() != null) {
            updateShooting(time);
        }
    }

    private void updatePathing(long time) {
        // update pathing
        mLastPathingTime += time;
        final int timing = mPath.getTiming(mLastPathingIndex);
        if (mLastPathingTime > timing) {
            if (mLastPathingIndex < mPath.getPathingCount() - 1) {
                // move to next pathing
                ++mLastPathingIndex;
                mLastPathingTime = 0;
            }
        }
        final Vertex2D nextPosition = mPath.getPositions(mLastPathingIndex);

        moveEnemy(nextPosition, time);
    }

    private void moveEnemy(final Vertex2D nextPosition, long time) {
        final float dx  = nextPosition.x - position.x;
        final float dy  = nextPosition.y - position.y;
        final float mag = (float)Math.sqrt(dx * dx + dy * dy);

        if (mag > 0 && mData.getVelocity() / mag * time < Vertex2D.range(nextPosition, position)) {
            mVelocity.x = dx / mag * mData.getVelocity();
            mVelocity.y = dy / mag * mData.getVelocity();
            // if moving upward, then adjust with tileset scrolling
            if (mVelocity.y < 0) {
                // TODO: remove this, this is super hack
                mVelocity.y -= 0.05f;
            }
        } else {
            mVelocity.x = 0;
            mVelocity.y = 0;
        }

        position.x += mVelocity.x * time;
        position.y += mVelocity.y * time;
    }

    private void updateShooting(long time) {
        final int reloadTime = mData.getReloadTime();
        if (mWaveReloadTime >= mData.getWaveReload()) {
            mReloadTime += time;
            if (mReloadTime > reloadTime) {
                if (mWaveShotCount < mData.getBulletPerWave()) {
                    mReloadTime -= reloadTime;
                    ++mWaveShotCount;

                    if (mTarget != null && mTarget.isAlive()) {
                        final int splitCount    = mData.getSplitCount();
                        final int spreadCount   = mData.getSpreadCount();

                        for (int i = 0; i < spreadCount; ++i) {
                            final int spreadAngle = -SPREAD_GAP / 2 * (spreadCount - 1) + i * SPREAD_GAP;
                            final double spreadRadian = Math.toRadians(spreadAngle);
                            int nowX    = 0;
                            int nowY    = 0;
                            final float dx1    = mTarget.position.x - position.x;
                            final float dy1    = mTarget.position.y - position.y;
                            float dx = dx1 * (float)Math.cos(spreadRadian) - dy1 * (float)Math.sin(spreadRadian);
                            float dy = dx1 * (float)Math.sin(spreadRadian) + dy1 * (float)Math.cos(spreadRadian);
                            float mag   = (float)Math.sqrt(dx * dx + dy * dy);
                            float bulletSpeed = 1000.0f / (float)mData.getBulletData().getDuration();
                            Vector2D velocity       = new Vector2D();

                            if (mag > 0 && bulletSpeed / mag * time < Vertex2D.range(mTarget.position, position)) {
                                velocity.x = -dx / mag * bulletSpeed;
                                // TODO: remove 0.05f part, that's super hack!
                                velocity.y = -dy / mag * bulletSpeed + 0.05f;
                                final int constant = splitCount == 1 ? 0 : -SPLIT_GAP * (splitCount - 1) / 2;
                                nowX = (int)((float)constant * dy / mag);
                                nowY = -(int)((float)constant * dx / mag);
                            } else {
                                velocity.x = 0;
                                velocity.y = 0;
                                dx = 1;
                                dy = 0;
                                mag = 1;
                            }
                            for (int j = 0; j < splitCount; ++j) {
                                Vertex2D newPosition = new Vertex2D(position);
                                newPosition.x   += nowX;
                                newPosition.y   += nowY;
                                nowX += (int)((float)SPLIT_GAP * dy / mag);
                                nowY -= (int)((float)SPLIT_GAP * dx / mag);
                                mBullets.addElement(new Bullet(mData.getBulletData(), newPosition, new Vector2D(velocity)));
                            }
                        }
                    }
                } else {
                    mWaveShotCount  = 0;
                    mWaveReloadTime = 0;
                    mReloadTime     = 0;
                }
            }
        } else {
            mWaveReloadTime += time;
        }

		updateBullets(time);
    }

	private void updateBullets(long time) {
		// update each bullets
		for (int i = 0, n = mBullets.size(); i < n; ++i) {
			Bullet bullet = (Bullet)mBullets.elementAt(i);

			bullet.update(time);
			if (!bullet.isActive()) {
				mDeleteBulles.addElement(bullet);
			}
		}

		// delete bullets
		for (int i = 0, n = mDeleteBulles.size(); i < n; ++i) {
			Bullet bullet = (Bullet)mDeleteBulles.elementAt(i);

			mBullets.removeElement(bullet);
		}
		mDeleteBulles.removeAllElements();
	}

	public void draw(Graphics g, int startX, int startY, int endX, int endY) {
		if (!mAlive || !mSpawned) return;

        // draw bullet
		drawBullets(g, startX, startY, endX, endY);
		//Get current sprite
		if (mSprite == null) return;

		//Get rect
		int Left	= (int)(position.x - startX );
		int Top		= (int)(position.y - startY );

        boolean shouldDraw = true;

		//Ensure in drawing area
		if (Left + mSprite.getWidth() < 0)					shouldDraw = false;
		else if(Left > StateManager.instance().getWidth())	shouldDraw = false;
		else if (Top + mSprite.getHeight() < 0)				shouldDraw = false;
		else if (Top > StateManager.instance().getHeight())	shouldDraw = false;

        if (!shouldDraw) {
            if (mLastPathingIndex >= mPath.getPathingCount() - 1) {
                mAlive = false;
                mShouldExplode = false;
            }
            return;
        }

		//Draw health bar
        //g.setColor(0x0040FF00);
        //g.fillRect(Left - 16, Top - 20, (int)((float)mHealth / (float)mData.getHealthMax() * (float)(mSprite.getWidth() - 8)), 4);

		//Draw sprite
		mSprite.setPosition(Left, Top);
		mSprite.draw(g, Graphics.VCENTER | Graphics.HCENTER);
	}

    private void drawBullets(Graphics g, int startX, int startY, int endX, int endY) {
		for (int i = 0, n = mBullets.size(); i < n; ++i) {
			Bullet bullet = (Bullet)mBullets.elementAt(i);

			bullet.draw(g, startX, startY, endX, endY);
		}
	}

	//Data
    protected Vector2D  mVelocity;
    protected EnemyData mData;
	protected Sprite	mSprite;

	// spawning things
	protected Path mPath;
    protected long mLastPathingTime;
    protected int mLastPathingIndex;
	protected int mSpawnTime;
	protected boolean mSpawned;
    private boolean mShouldExplode;
    private int mPickupData;

    // shooting things
    protected Player mTarget;
    protected int mReloadTime;
    protected int mWaveReloadTime;
    protected int mWaveShotCount;
	private Vector mBullets;
	private Vector mDeleteBulles;

    private static final int SPLIT_GAP  = 8;
    private static final int SPREAD_GAP = 180 / Utilities.MAX_SHOT_SPREAD;
}
